<?php

class Engine_DB_Command extends Engine_Db_Object
{
    const CMD_FILE_FOLDER_DEFAULT = '/application/modules/';
    const CMD_FILE_EXT_DEFAULT = '.sql.phtml';

	public static $_commandStack = array();

	protected
		$query = '',
        $cache = false,
        $cacheSid = null,
        $where = null,
        $vars = array
        (
            'execmethod' => 'getAll',
            'cmdfile' => null,
            'cmd' => null,
		)
	;

	/**
	 * @var Engine_Adapter
	 */
	protected $_adapter;

	/**
	 * @param Engine_Adapter $adapter
	 * @param string $where
	 * @param array $vars
	 */
	public function __construct($adapter, $where, $vars = array())
	{
        parent::__construct();

        $this->_adapter = $adapter;
        $this->where = $where;
        $this->vars = array_merge($this->vars, $vars);

		$where = explode('::', $this->where);

		$entity = $where[0];
		$command = $where[1];

		$this->fetchIntoDefaultSet('Engine_Core_Wrapper');
		$this->fetchIntoCollectionDefaultSet('Engine_Core_Collection');

		if (!empty($vars['fetchInto']))
		{
			$this->fetchIntoDefaultSet($vars['fetchInto']);
			$this->fetchIntoCollectionDefaultSet($vars['fetchIntoCollection']);
		}
		else
		{
			if (file_exists(APPLICATION_PATH . $entity . '/Wrapper.php'))
			{
				$this->fetchIntoDefaultSet($entity . '_Wrapper');
			}

			if (file_exists(APPLICATION_PATH . $entity . '/Collection.php'))
			{
				$this->fetchIntoCollectionDefaultSet($entity . '_Collection');
			}
		}
	}

	/**
	 * @static
	 * @param  $where
	 * @param  $params
	 * @return Engine_DB_Command
	 */
	static public function factory($adapter, $where, $params = array())
	{
		$dbCommand = new self($adapter, $where, $params);

		return $dbCommand;
	}

    public function &__call($mthd, $args)
    {
        try
        {
            return parent::__call($mthd, $args);
        }
        catch (DB_Object_Exception $e)
        {
            if (!method_exists($this, $mthd = "_{$mthd}"))
            {
                throw new DB_Command_Method_Exception("Method '$mthd' doesn't exists in " . get_class($this));
            }

            $vars = array();
            foreach ($args AS $arg)
            {
                foreach ($arg AS $k => $v)
                {
                    $vars[$k] = $v;
                }
            }

            call_user_func(array($this, "{$mthd}"), $vars);
            return $this;
        }
    }

    protected function _apply($vars = array())
    {
        $this->vars = array_merge($this->vars, $vars);
    }

    protected function _cache($sid = null)
    {
        $this->cache = false;
        $this->cacheSid = null;
        if ($sid)
        {
            $this->cache = true;
            $this->cacheSid = $sid;
        }
    }

	/**
	 * @return Engine_Core_Collection|mixed
	 */
    public function exec()
    {
		$this->execmethodPrepare();
        $this->cmdPrepare();

        $time = microtime(true);

        $result = call_user_func(array($this, $this->vars['execmethod']), $this->vars['cmd']);

	    $time = microtime(true) - $time;

	    self::$_commandStack[] = array(
			'command' => $this->where,
	        'params'  => $this->vars,
	        'time'    => $time
	    );

	    return $result;
    }

	/**
	 * @param string $column default id
	 * @return mixed[]
	 */
	public function fetchCol($column = 'id')
	{
		$data = array();

		foreach ($this->exec() as $item) {
			$data[] = $item->$column;
		}

		return $data;
	}

	/**
	 * @param string $column default id
	 * @return mixed
	 */
	public function fetchOne($column = 'id')
	{
		$data = null;

		foreach ($this->exec() as $item) {
			$data = $item->$column;

			break;
		}

		return $data;
	}

	/**
	 * @return Engine_Core_Collection
	 */
	public function fetchAll()
	{
		return $this->exec();
	}

	public function getCountAll($print = false)
	{
		
		$sql =
			'SELECT COUNT(*) FROM (' .
			preg_replace(array('/#fields.*#\/fields/ims', '/#limit.*#\/limit/ims'), array('1', ''), $this->vars['cmd']) .
			') as `robocounter_' . $this->where . '`';

		if ($print) {
			print_r($this->vars['cmd'] . "\n");
			print_r($sql . "\n");
		}

		return $this->getOne($sql);
	}

	public function toString()
	{
		$this->cmdPrepare();

		return $this->vars['cmd'];
	}

    protected function execmethodPrepare()
    {
        if (!$this->vars['execmethod'])
        {
            $this->vars['execmethod'] = 'getAll';
        }
    }

	protected function cmdPrepare()
	{
		$dirName = explode('_', str_replace('::', '_', $this->where));

		$fileName = array_pop($dirName) . self::CMD_FILE_EXT_DEFAULT;

		$dirName
			= (strpos($this->where, 'core') !== 0)
			? ROOT . self::CMD_FILE_FOLDER_DEFAULT . implode('/', $dirName) . '/'
			: APPLICATION_PATH . '/' . implode('/', $dirName) . '/'
		;

		$this->vars['cmdfile'] = $this->vars['cmdfile'] ? $this->vars['cmdfile'] : $dirName . $fileName;

		if (!$this->vars['cmd'] && !($this->vars['cmdfile'] && file_exists($this->vars['cmdfile'])))
		{
			throw new DB_Command_Command_Exception("Command flesh (" . $this->vars['cmdfile'] . ") in '{$this->where}' doesn't exists");
		}

		$this->vars['cmd'] = $this->render($fileName, $this->vars, $dirName);

//		var_dump($this->vars['cmd']);
	}

	protected function render($template, $params, $directory)
	{
		$view = new Zend_View();
		$view->setScriptPath($directory);

		$params['adapter'] = $this->_adapter;
		
		return $view->partial($template, $params);
	}

	public function prepare()
	{
		return $this->cmdPrepare();
	}
}

class DB_Command_Command_Exception extends Exception
{}